/**
 * 
 */
package br.com.beyondclick.model.entity;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

/**
 * @author AC de Souza
 */
@Entity
public class CompraMaterial implements Serializable {
	private static final long serialVersionUID = 1L;

	@Id @GeneratedValue
	private Long id;
	private long quantidade;
	private BigDecimal preco;

	@ManyToOne(optional=false) private Compra compra;
	@ManyToOne(optional=false) private Material material;

	@OneToMany(mappedBy="compraMaterial", cascade=CascadeType.ALL)
	private List <CompraPedidoMaterial> compraPedidoMateriais;

	/**
	 * Realizar a compra que atenda aos objetos {@link PedidoMaterial} para o mesmo {@link Material} deste objeto {@link CompraMaterial}.
	 * 
	 * @param pedidosMateriaisPendentes Objetos {@link PedidoMaterial}, pendentes de compra. E que solicitaram o mesmo {@link Material} deste {@link CompraMaterial}.
	 * @return Os objetos {@link PedidoMaterial} que não foram atendidos por esta {@link CompraMaterial}.
	 */
	public List<PedidoMaterial> atender(List<PedidoMaterial> pedidosMateriaisPendentes){
		List<PedidoMaterial> pedidosMateriaisPendentesNaoAtendidos = new ArrayList<PedidoMaterial>();
		PedidoMaterial pedidoMaterialPendente = null;
		long quantidadePendente = 0;
		long quantidadeCompradaNaoUsada = this.quantidadeEmEstoque > 0 ? this.quantidadeEmEstoque : this.quantidade;

		for ( int i=0; i < pedidosMateriaisPendentes.size() && quantidadeCompradaNaoUsada > 0; i++ ) {
			pedidoMaterialPendente = pedidosMateriaisPendentes.get(i);

			if ( this.getMaterial().equals(pedidoMaterialPendente.getMaterial()) ) {
				quantidadePendente = pedidoMaterialPendente.getQuantidadePendente();
				if ( quantidadePendente >= quantidadeCompradaNaoUsada ){
					addCompraPedidoMaterial( buildCompraPedidoMaterial(this, pedidoMaterialPendente, quantidadeCompradaNaoUsada) );
					quantidadeCompradaNaoUsada = 0L;
				}else{
					addCompraPedidoMaterial( buildCompraPedidoMaterial(this, pedidoMaterialPendente, quantidadePendente) );
					quantidadeCompradaNaoUsada = quantidadeCompradaNaoUsada - quantidadePendente;
				}
			}else{
				pedidosMateriaisPendentesNaoAtendidos.add(pedidoMaterialPendente);
			}
		}
		assert quantidadeCompradaNaoUsada < 0 : "Quantidade comprada atingiu valor negativo: "+ quantidadeCompradaNaoUsada +", PedidoMaterialId: "+ pedidoMaterialPendente.getId();

		return pedidosMateriaisPendentesNaoAtendidos;
	}
	private long quantidadeEmEstoque = 0L;
	/**
	 * Usado na gravação do {@link Pedido} para informar a quantidade já comprada.
	 * 
	 * @param quantidadeComprada
	 */
	public void setQuantidadeEmEstoque(long quantidadeEmEstoque){
		this.quantidadeEmEstoque = quantidadeEmEstoque;
	}
	private CompraPedidoMaterial buildCompraPedidoMaterial(final CompraMaterial compraMaterial, final PedidoMaterial pedidoMaterial, final long quantidadeComprada){
		return new CompraPedidoMaterial(compraMaterial, pedidoMaterial, quantidadeComprada);
	}
	
	/**
	 * Adiciona um material a lista de materiais desde pedido.
	 * Caso o material já tenha sido adicionado, irá incrementar a quantidade.
	 * 
	 * @param materiaisPedidos the materiaisPedidos to set
	 */
	public void addCompraPedidoMaterial(CompraPedidoMaterial compraPedidoMaterial) {
		if( this.compraPedidoMateriais == null )
			this.compraPedidoMateriais = new ArrayList<CompraPedidoMaterial>();

		this.compraPedidoMateriais.add(compraPedidoMaterial);
	}
	/**
	 * Adiciona uma lista de material a lista de materiais desde pedido.
	 * Caso o material já tenha sido adicionado, irá incrementar a quantidade.
	 * 
	 * @param materiaisPedidos the materiaisPedidos to set
	 */
	public void addAllCompraPedidoMaterial(List<CompraPedidoMaterial> compraPedidoMaterial) {
		if( this.compraPedidoMateriais == null )
			this.compraPedidoMateriais = new ArrayList<CompraPedidoMaterial>();

		this.compraPedidoMateriais.addAll(compraPedidoMaterial);
	}

	/**
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int PRIME = 31;
		int result = 1;
		result = PRIME * result + (int) (id ^ (id >>> 32));
		return result;
	}

	/**
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final CompraMaterial other = (CompraMaterial) obj;
		if (id != other.id)
			return false;
		return true;
	}

	/**
	 * @return the compra
	 */
	public Compra getCompra() {
		return compra;
	}

	/**
	 * @param compra the compra to set
	 */
	public void setCompra(Compra compra) {
		this.compra = compra;
	}

	/**
	 * @return the compraPedidoMateriais
	 */
	public List<CompraPedidoMaterial> getCompraPedidoMateriais() {
		return compraPedidoMateriais;
	}

	/**
	 * @param compraPedidoMateriais the compraPedidoMateriais to set
	 */
	public void setCompraPedidoMateriais(
			List<CompraPedidoMaterial> compraPedidoMateriais) {
		this.compraPedidoMateriais = compraPedidoMateriais;
	}

	/**
	 * @return the id
	 */
	public Long getId() {
		return id;
	}

	/**
	 * @param id the id to set
	 */
	public void setId(Long id) {
		this.id = id;
	}

	/**
	 * @return the preco
	 */
	public BigDecimal getPreco() {
		return preco;
	}

	/**
	 * @param preco the preco to set
	 */
	public void setPreco(BigDecimal preco) {
		this.preco = preco;
	}

	/**
	 * @return the quantidade
	 */
	public long getQuantidade() {
		return quantidade;
	}

	/**
	 * @param quantidade the quantidade to set
	 */
	public void setQuantidade(long quantidade) {
		this.quantidade = quantidade;
	}
	
    /**
     * @return the material
     */
    public Material getMaterial() {
    	return material;
    }
	
    /**
     * @param material the material to set
     */
    public void setMaterial(Material material) {
    	this.material = material;
    }
}